﻿using System;
using System.Collections.Generic;
using System.Text;
using Cartography.Desktop;
using System.Xml;

namespace Cartography.Desktop
{
    public struct Valores
    {
        public double xMin, xMax, yMin, yMax;
    }
    class Poligono : System.Windows.Forms.PictureBox
    {
        private LatLon[] coordenadas;
        private int numeroDePuntos;
        private Cartography.Desktop.LatLon puntoUltimo;
        private Boolean completo;
        private int[] XY;

        public int NumeroDePuntos
        {
            get
            {
                return this.numeroDePuntos;
            }
        }
        public Boolean Completo
        {
            get
            {
                return this.completo;
            }
        }
        public Cartography.Desktop.LatLon[] Coordenadas
        {
            get
            {
                return this.coordenadas;
            }
        }

        public  Poligono() {
            numeroDePuntos = 0;
            coordenadas = new LatLon[1];
            puntoUltimo=null;
            completo = false;
            XY = new int[2];
            //this.Click += new EventHandler(this.clickHandler);
        }
        public Poligono(Cartography.Desktop.LatLon[] coordenadas)
        {
            this.coordenadas = coordenadas;
            numeroDePuntos = this.coordenadas.Length;
            puntoUltimo = this.coordenadas[0];
            completo = true;
            XY = new int[2];
        }

        public Boolean agregarPunto(Cartography.Desktop.LatLon coordenadas2, int[] XY2)
        {
            if (!completo)
            {
                numeroDePuntos += 1;
                Array.Resize(ref this.coordenadas, this.numeroDePuntos);
                this.coordenadas[numeroDePuntos - 1] = coordenadas2;
                if (puntoUltimo == null) this.XY = XY2;
                this.puntoUltimo = coordenadas2;
                if (((XY == XY2) || ((XY2[0] > XY[0] - 5 && XY2[0] < XY[0] + 5) && (XY2[1] > XY[1] - 5 && XY2[1] < XY[1] + 5))) && (numeroDePuntos > 1))
                {
                    this.completo = true;
                    this.coordenadas[numeroDePuntos - 1] = this.coordenadas[0];
                }
            }
            return this.completo;
        }
        public void borrarPoligonoEnEdicion() {
            numeroDePuntos = 0;
            coordenadas = new LatLon[1];
            puntoUltimo = null;
            completo = false;
            XY = new int[2];
        }
        public double calcularArea() {
            if (this.numeroDePuntos < 3) return 0.0;
            double sum = 0.0;

            double ax = this.coordenadas[0].Longitude;
            double ay = this.coordenadas[0].Latitude;
            for (int i = 1; i < this.numeroDePuntos - 2; i++)
            {
                double bx = this.coordenadas[i].Longitude;
                double by = this.coordenadas[i].Latitude;
                double cx = this.coordenadas[i+1].Longitude;
                double cy = this.coordenadas[i+1].Latitude;
                sum += ax * by - ay * bx +
                    ay * cx - ax * cy +
                    bx * cy - cx * by;
            }
            return -sum / 2.0;
        }
        public double[] calcularCentroide() {

            //X = S ((y(i) - y(i+1)) (x(i)2 + x(i)x(i+1) + x(i+1)2)/6A) 
            //Y = S ((x(i+1) - x(i)) (y(i)2 + y(i)y(i+1) + y(i+1)2)/6A) 
            //where A is the area of the polygon 
            //Supuestamente esta funcion me garantiza q el centroide queda dentro del poligono
            double area, sumaX, sumaY, tempX, tempY ;
            area= sumaX= sumaY= tempX= tempY=0;
            LatLon[] temp = cambiarLatitudes();
            if (completo)
            {
                
                area = this.calcularArea();
                for (int i = 0; i < numeroDePuntos-2; i++)
                {
                    tempX = ((temp[i].Latitude - temp[i + 1].Latitude) * ((temp[i].Longitude * 2) + (temp[i].Longitude * temp[i + 1].Longitude) + (temp[i + 1].Longitude * 2))) / (6 * area);
                    tempY = ((temp[i + 1].Longitude - temp[i].Longitude) * ((temp[i].Latitude * 2) + (temp[i].Latitude * temp[i + 1].Latitude) + (temp[i + 1].Latitude * 2))) / (6 * area);

                    sumaX += tempX;
                    sumaY += tempY;
                }
            }
            return new Double[2] { sumaX, (sumaY-( Math.Abs( minY())+1)) };
        }
        public double[] calcularCentroidePromedio()
        {

            //Calculo el centroide en base al promedio de los vertices
            double sumaX, sumaY;
             sumaX = sumaY = 0;
            if (completo)
            {
                for (int i = 0; i < numeroDePuntos - 1; i++)
                {
                    sumaX += coordenadas[i].Longitude;
                    sumaY += coordenadas[i].Latitude;
                }
                sumaX /= (numeroDePuntos - 1);
                sumaY /= (numeroDePuntos - 1);
            }

            return new Double[2] { sumaX, sumaY  };
        }
        private double minY() {
            // Calcula cual es el valor minimo de las latitudes de las coordenadas
            double resultado=0;

            for (int i = 0; i < numeroDePuntos; i++) {
                if (i == 0)
                {
                    resultado = coordenadas[i].Latitude;
                }
                else {
                    if (resultado > coordenadas[i].Latitude) resultado = coordenadas[i].Latitude;
                }
            }

                return resultado;
        }
        private Cartography.Desktop.LatLon[] cambiarLatitudes()
        {
            LatLon[] resultado = new LatLon[numeroDePuntos];
            double minimo= Math.Abs( minY())+1;
            resultado=coordenadas;
            for(int i=0; i<numeroDePuntos;i++){
                resultado[i].Latitude += minimo;
            }
            return resultado;
        }
        public void dibujarPoligono(Map mapa, String estado)
        {
            System.Drawing.Point[] puntos = new System.Drawing.Point[this.NumeroDePuntos];
            System.Drawing.Point[] puntosTransformados = new System.Drawing.Point[this.NumeroDePuntos];
            int[] punto;
            Valores valores = calcularValores(mapa); // calcula los valores min y max de la x,y
            int tamX, tamY;
            //obtenemos el tamano q deberia tener el bitmap
            tamX = Convert.ToInt32(valores.xMax - valores.xMin);
            tamY = Convert.ToInt32(valores.yMax - valores.yMin);
            
            //obtenemos todos los puntos transformados del poligono a coordenadas del mapa
            for (int i = 0; i < this.NumeroDePuntos; i++)
            {
                punto = mapa.toZoomedPixelCoords(this.Coordenadas[i], mapa.getActualZoom);
                int positionScreenX2 = mapa.getWidth / 2 + (punto[0] - mapa.CentralTilePixel[0]);
                int positionScreenY2 = mapa.getHeight / 2 + (punto[1] - mapa.CentralTilePixel[1]);
                puntos[i].X = positionScreenX2;
                puntos[i].Y = positionScreenY2;
            }

            System.Drawing.Bitmap image = new System.Drawing.Bitmap(tamX, tamY);
            System.Drawing.Graphics canvas = System.Drawing.Graphics.FromImage(image);
            
            //transformamos los puntos del mapa a puntos del bitmap y luego dibujamos
            puntosTransformados = this.transformarPuntos(puntos, Convert.ToInt32(valores.xMin), Convert.ToInt32(valores.yMin));
            
            switch (estado)
            { 
                case "Emergencia":
                    canvas.FillPolygon(new System.Drawing.SolidBrush(System.Drawing.Color.Orange), puntosTransformados);
                    canvas.DrawPolygon(new System.Drawing.Pen(System.Drawing.Color.Orange), puntosTransformados);
                    break;
                case "Seleccion":
                    canvas.FillPolygon(new System.Drawing.SolidBrush(System.Drawing.Color.LightGreen), puntosTransformados);
                    canvas.DrawPolygon(new System.Drawing.Pen(System.Drawing.Color.LightGreen), puntosTransformados);
                    break;
                default:
                    canvas.FillPolygon(new System.Drawing.SolidBrush(System.Drawing.Color.Blue), puntosTransformados);
                    canvas.DrawPolygon(new System.Drawing.Pen(System.Drawing.Color.Blue), puntosTransformados);
                    break;
            }

            this.Image = image;
            this.Location = new System.Drawing.Point(Convert.ToInt32(valores.xMin), Convert.ToInt32(valores.yMin));
            this.Size = this.Image.Size;

        }
        public void puntosDeInteresEdificio(string grupo, Map mapa)
        {

                //Nodos de los puntos de interes
                XmlDocument doc = new XmlDocument();
                System.Globalization.NumberFormatInfo nfi = new System.Globalization.CultureInfo("en-US", false).NumberFormat;
                doc.Load(mapa.PathData + "\\PointsOfInterest\\" + grupo + ".xml");
                XmlNode redSeca = doc.SelectSingleNode("PointsOfInterest/Edificios/Edificio/RedSeca");
                XmlNode redInerte = doc.SelectSingleNode("PointsOfInterest/Edificios/Edificio/RedInerte");
                XmlNode viaEvacuacion = doc.SelectSingleNode("PointsOfInterest/Edificios/Edificio/ViaEvacuacion");
                //Iconos de los puntods de interes
                System.Drawing.Bitmap tmpDibujar = new System.Drawing.Bitmap(mapa.PathData + "\\Icons\\redElectricaInerte.png");
                System.Drawing.Bitmap iconRedInerte = new System.Drawing.Bitmap(tmpDibujar);
                System.Drawing.Bitmap iconRedSeca = new System.Drawing.Bitmap(new System.Drawing.Bitmap(mapa.PathData + "\\Icons\\redSecaEntrada.png"));
                System.Drawing.Bitmap iconViaEvacuacion = new System.Drawing.Bitmap(new System.Drawing.Bitmap(mapa.PathData + "\\Icons\\viaEvacuacion.png"));

                string Latitud, Longitud;
                int[] punto;
                int positionScreenX;
                int positionScreenY;
                System.Drawing.Graphics canvas = System.Drawing.Graphics.FromImage(this.Image);
                if ((redSeca.SelectSingleNode("Latitud").InnerText != null) && (redSeca.SelectSingleNode("Longitud").InnerText != null))
                {
                    Latitud = redSeca.SelectSingleNode("Latitud").InnerText;
                    Longitud = redSeca.SelectSingleNode("Longitud").InnerText;
                    punto = mapa.toZoomedPixelCoords(new LatLon(Latitud, Longitud), mapa.getActualZoom);
                    positionScreenX = mapa.getWidth / 2 + (punto[0] - mapa.CentralTilePixel[0]);
                    positionScreenY = mapa.getHeight / 2 + (punto[1] - mapa.CentralTilePixel[1]);
                    canvas.DrawImage(iconRedSeca, positionScreenX-this.Location.X, positionScreenY-this.Location.Y);
                }
                if ((redInerte.SelectSingleNode("Latitud").InnerText != null) && (redInerte.SelectSingleNode("Longitud").InnerText != null))
                {
                    Latitud = redInerte.SelectSingleNode("Latitud").InnerText;
                    Longitud = redInerte.SelectSingleNode("Longitud").InnerText;
                    punto = mapa.toZoomedPixelCoords(new LatLon(Latitud, Longitud), mapa.getActualZoom);
                    positionScreenX = mapa.getWidth / 2 + (punto[0] - mapa.CentralTilePixel[0]);
                    positionScreenY = mapa.getHeight / 2 + (punto[1] - mapa.CentralTilePixel[1]);
                    canvas.DrawImage(iconRedInerte, positionScreenX - this.Location.X, positionScreenY - this.Location.Y);
                }
                if ((viaEvacuacion.SelectSingleNode("Latitud").InnerText != null) && (viaEvacuacion.SelectSingleNode("Longitud").InnerText != null))
                {
                    Latitud = viaEvacuacion.SelectSingleNode("Latitud").InnerText;
                    Longitud = viaEvacuacion.SelectSingleNode("Longitud").InnerText;
                    punto = mapa.toZoomedPixelCoords(new LatLon(Latitud, Longitud), mapa.getActualZoom);
                    positionScreenX = mapa.getWidth / 2 + (punto[0] - mapa.CentralTilePixel[0]);
                    positionScreenY = mapa.getHeight / 2 + (punto[1] - mapa.CentralTilePixel[1]);
                    canvas.DrawImage(iconViaEvacuacion, positionScreenX - this.Location.X, positionScreenY - this.Location.Y);
                }

        }
        private System.Drawing.Point[] transformarPuntos(System.Drawing.Point[] puntos, int xMin, int yMin) {
            System.Drawing.Point[] puntosTransformados = new System.Drawing.Point[this.NumeroDePuntos];
            for (int i = 0; i < numeroDePuntos; i++) {
                puntosTransformados[i].X = puntos[i].X - xMin;
                puntosTransformados[i].Y = puntos[i].Y - yMin; ;
            }
            return puntosTransformados;
        }
        private Valores calcularValores(Map mapa) {
            Valores resultado;
            int[] punto;
            
            punto = mapa.toZoomedPixelCoords(this.Coordenadas[0], mapa.getActualZoom);
            int positionScreenX = mapa.getWidth / 2 + (punto[0] - mapa.CentralTilePixel[0]);
            int positionScreenY = mapa.getHeight / 2 + (punto[1] - mapa.CentralTilePixel[1]);
            resultado.xMax = resultado.xMin =positionScreenX;
            resultado.yMax = resultado.yMin = positionScreenY;
            for (int i = 1; i < numeroDePuntos; i++) {
                punto = mapa.toZoomedPixelCoords(this.Coordenadas[i], mapa.getActualZoom);
                int positionScreenX2 = mapa.getWidth / 2 + (punto[0] - mapa.CentralTilePixel[0]);
                int positionScreenY2 = mapa.getHeight / 2 + (punto[1] - mapa.CentralTilePixel[1]);

                if (resultado.xMax < positionScreenX2)  resultado.xMax = positionScreenX2;
                if (resultado.xMin > positionScreenX2)  resultado.xMin = positionScreenX2;
                if (resultado.yMax < positionScreenY2)  resultado.yMax = positionScreenY2;
                if (resultado.yMin > positionScreenY2)  resultado.yMin = positionScreenY2;

            }
            return resultado;
        }
       
    }
}
